#ifndef INCLUDE_SQL_CONNECTOR_H_
#define INCLUDE_SQL_CONNECTOR_H_

#include <iostream>
#include <sstream>
#include <string>
#include <thread>

#include "/usr/local/Cellar/mysql-client/8.0.25/include/mysql/mysql.h"

/**
 * @brief SQL连接函数
 *
 */
class SQLConnector {
 public:
  SQLConnector();
  ~SQLConnector();
  bool initDB(const std::string &host, const std::string &user,
              const std::string &passwd, const std::string &db_name,
              int port = 3306);

  void Execute(
      const std::string &cmd,
      std::function<void(MYSQL_RES *)> fn = [](MYSQL_RES *) {});

 private:
  MYSQL *mysql;
};

inline SQLConnector::SQLConnector() : mysql(nullptr) {
  mysql = new MYSQL;
  mysql = mysql_init(mysql);
  if (!mysql) {
    std::string str = mysql_error(mysql);
    mysql_close(mysql);
    delete mysql;
    throw std::runtime_error(std::string("mysql pointer init error: %s\n") +
                             str);
  }
}

inline SQLConnector::~SQLConnector() {
  if (mysql) {
    mysql_close(mysql);
    delete mysql;
  }
}

/**
 * @brief 连接数据库的函数，返回是否成功
 *
 * @param host 主机
 * @param user 用户
 * @param passwd 密码
 * @param db_name 数据库名
 * @param port 端口
 * @return true
 * @return false
 */
inline bool SQLConnector::initDB(const std::string &host,
                                 const std::string &user,
                                 const std::string &passwd,
                                 const std::string &db_name, int port) {
  if (mysql) mysql_close(mysql);
  mysql = mysql_real_connect(mysql, host.c_str(), user.c_str(), passwd.c_str(),
                             db_name.c_str(), port, NULL, 0);
  if (!mysql) {
    std::string str = mysql_error(mysql);
    mysql_close(mysql);
    delete mysql;
    throw std::runtime_error(std::string("mysql open error: %s\n") + str);
  }
  return true;
}

/**
 * @brief 通用的执行函数
 *
 * @param cmd SQL命令
 * @param fn 回掉函数，用于返回结果
 */
inline void SQLConnector::Execute(const std::string &cmd,
                                  std::function<void(MYSQL_RES *)> fn) {
  const size_t MAX_TRIAL = 16;
  // char *zErrMsg = 0;
  int rc = 0;
  for (size_t iTry = 0; iTry < MAX_TRIAL; iTry++) {
    // mysql_query()执行成功返回0，执行失败返回非0值
    rc = mysql_query(mysql, cmd.c_str());
    if (!rc) break;
    std::this_thread::sleep_for(std::chrono::microseconds(20));
  }

  if (rc) {
    std::string str = mysql_error(mysql);
    mysql_close(mysql);
    // delete mysql;
    std::cout << "exec sql : " + cmd + " -> error" << std::endl;
    throw std::runtime_error(std::string("mysql exec error: %s\n") + str);
  } else {
    std::cout << "exec sql : " + cmd + " -> ok;" << std::endl;
  }

  fn(mysql_store_result(mysql));
}

/**
 * @brief 序列化
 *
 * @tparam T
 * @param os 输出
 * @param value 输入
 * @return std::ostream&
 */
template <typename T>
inline std::ostream &SerializeValue(std::ostream &os, const T &value) {
  return os << value;
}

template <>
inline std::ostream &SerializeValue<std::string>(std::ostream &os,
                                                 const std::string &value) {
  return os << "'" << value << "'";
}

/**
 * @brief 反序列化
 *
 * @tparam T
 * @param property 输出
 * @param value 输入
 */
template <typename T>
inline void DeserializeValue(T &property, std::string value) {
  std::stringstream ostr(value);
  ostr >> property;
}

template <>
inline void DeserializeValue<std::string>(std::string &property,
                                          std::string value) {
  property = std::move(value);
}

/**
 * @brief 类型判断
 *
 * @tparam T 输入类型
 * @param t 数据
 * @return const char* 数据类型
 */
template <typename T>
const char *TypeString(T &t) {
  constexpr const char *typeStr =
      (std::is_integral<T>::value &&
       !std::is_same<std::remove_cv<T>, char>::value &&
       !std::is_same<std::remove_cv<T>, wchar_t>::value &&
       !std::is_same<std::remove_cv<T>, char16_t>::value &&
       !std::is_same<std::remove_cv<T>, char32_t>::value &&
       !std::is_same<std::remove_cv<T>, unsigned char>::value)
          ? "INT"
      : (std::is_floating_point<T>::value)                      ? "DOUBLE"
      : (std::is_same<std::remove_cv_t<T>, std::string>::value) ? "VARCHAR(40)"
                                                                : nullptr;

  static_assert(typeStr != nullptr,
                "Only Support Integral, Floating Point and std::string :-)");
  return typeStr;
}

/**
 * @brief 访问者类
 *
 */
class FnVisitor {
 public:
  template <typename Fn, typename... Args>
  inline void Visit(Fn fn, Args &...args) const {
    _Visit(fn, args...);
  }

 protected:
  /**
   * @brief 访问执行
   *
   * @tparam Fn
   * @tparam T
   * @tparam Args
   * @param fn 回掉函数
   * @param property 当前参数
   * @param args 剩余参数
   */
  template <typename Fn, typename T, typename... Args>
  inline void _Visit(Fn fn, T &property, Args &...args) const {
    if (_Visit(fn, property)) _Visit(fn, args...);
  }
  template <typename Fn, typename T>
  inline bool _Visit(Fn fn, T &property) const {
    return fn(property);
  }
};

#endif  // INCLUDE_SQL_CONNECTOR_H_
